package cz.muni.fi.pb138.webrep.repositories;

import cz.muni.fi.pb138.webrep.util.Pair;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import org.basex.core.BaseXException;
import org.basex.core.Context;
import org.basex.core.cmd.Close;
import org.basex.core.cmd.CreateDB;
import org.basex.core.cmd.Open;
import org.basex.core.cmd.XQuery;

/**
 *
 * @author Max
 */

//Class represents web archive metadata repository
public class WarRepository {
    private String warDBName;
    
    //open existing db or create empty
    public WarRepository() throws IOException {
        Properties p = new Properties();
        p.load(this.getClass().getResourceAsStream("../db_config.properties"));
        warDBName = p.getProperty("db.war.name");
        Context context = new Context();
        try {
            new Open(warDBName).execute(context);
        } catch(BaseXException e) {
            new CreateDB(warDBName, "<archives/>").execute(context);
        } finally {
            new Close().execute(context);
        }
    }
    
    
    //adds list of listeners names 
    //and list of filters names
    //into db for current archive 
    //@filename is given filepath to an archive
    //@name is in format "archivename-version"
    public void addWarRecord(String filepath, String name) throws BaseXException {
        filepath = filepath.replace("'", "&#39;");
        name = name.replace("'", "&#39;");
        Context context = new Context();
        try {
            //Open DB
            new Open(warDBName).execute(context);
            //Insert new web archive name into DB
            String query = "if (empty(/archives/archive[@filename='"+ name +"']))\n"
                    + "then insert node <archive filename='"+ name +"'/>"
                    + " into /archives"
                    + " else ()";
            new XQuery(query).execute(context);            
            
            //Adds listeners of current archive into DB
            query = "declare namespace lns = \"http://xmlns.jcp.org/xml/ns/javaee\";\n"+
                    "let $listeners := doc('"+filepath+".xml')//lns:listener-class\n"+
                    "for $listener in $listeners\n"+
                    "\t return (insert node <listener name=\"{$listener/text()}\"/>"+
                    "\t into /archives/archive[@filename='"+ name +"']\n)";
            new XQuery(query).execute(context);
            
            //Adds filters of current archive into DB
            query = "declare namespace fns = \"http://xmlns.jcp.org/xml/ns/javaee\";\n"+
                    "let $filters := doc('"+filepath+".xml')//fns:filter-class\n"+
                    "for $filter in $filters\n"+
                    "\t return (insert node <filter name=\"{$filter/text()}\"/>"+
                    "\t into /archives/archive[@filename='"+ name +"']\n)";
            new XQuery(query).execute(context);
        }
        finally {
            //Close DB
            new Close().execute(context);
        }
    }
    
    //delete metadata artifact with "name" from db
    //@name in format "arhivename"
    //so it deletes all versions of archive
    public void deleteRecord(String name) throws BaseXException{
        Context context = new Context();
        new Open(warDBName).execute(context);
        try {
            new XQuery("for $archive in //archive\n" + 
                    "where substring(data($archive/@filename), 1, '"+
                    name.length()+"') = '" + name + "'\n" + 
                    "return (delete node $archive)")
                    .execute(context);
        }
        finally {
            new Close().execute(context);
        }
    }
    
    //returns list of listeners names of archive "filename"
    public List<String> getListOfListeners(String filename) throws BaseXException {
        filename = filename.replace("'", "&#39;");
        Context context = new Context();
        List<String> listeners = new ArrayList();
        try {
            new Open(warDBName).execute(context);
            String query = 
                    "for $listener in /archives/archive[@filename='"+ filename +"']//listener\n"+
                    "return data($listener/@name)";
            String str = new XQuery(query).execute(context);
            if (!str.equals(""))
            listeners.addAll(Arrays.asList(str.split(" ")));
        }
        finally {
            new Close().execute(context);
        }
        return Collections.unmodifiableList(listeners);
    }
    
    
    //returns list of filters names of archive "filename"
    public List<String> getListOfFilters(String filename) throws BaseXException {
        filename = filename.replace("'", "&#39;");
        Context context = new Context();
        List<String> filters = new ArrayList();
        try {
            new Open(warDBName).execute(context);
            String query = 
                    "for $filter in /archives/archive[@filename='"+ filename +"']//filter\n"+
                    "return data($filter/@name)";
            String str = new XQuery(query).execute(context);
            if (!str.equals(""))
            filters.addAll(Arrays.asList(str.split(" ")));
        }
        finally {
            new Close().execute(context);
        }
        return Collections.unmodifiableList(filters);
    }
    
    //returns map of arvhives and versions with given listeners and filters
    //which contains in listOfPairs
    public Map<String, Set<Integer>> getByMetadata (List<Pair<String, String>> listOfPairs) throws BaseXException{
        Context context = new Context();
        Map<String, Set<Integer>> archives = new HashMap();
        List<String> list = new ArrayList(), cache = new ArrayList(), tmp = new ArrayList();
        try {
            new Open(warDBName).execute(context);
            int i = 0;
            for (Pair<String, String> pair : listOfPairs) {
                i++;
                String query = 
                    "for $archive in /archives//archive\n"+
                    "where $archive/"+pair.getLeft()+"/@name='"+pair.getRight()+"'\n"+
                    "return data($archive/@filename)\n";
                String str = new XQuery(query).execute(context);
                if (str.equals("")) return archives;
                //Tento cyklus je pro prunik nekolika mnozin
                cache.addAll(Arrays.asList(str.split(" ")));
                if (i == 1) {list.addAll(cache); cache.clear();}
                else {
                    tmp.addAll(list);
                    list.clear();
                    for (String row : cache) {
                        if (tmp.contains(row)) list.add(row);
                    }
                    tmp.clear(); cache.clear();
                }
            }
            //from list to map
            for (String archiveName : list){
                    int position = archiveName.lastIndexOf("-");
                    String version = archiveName.substring(position+1);
                    archiveName = archiveName.substring(0, position);
                   if (archives.containsKey(archiveName)){
                        archives.get(archiveName).add(Integer.parseInt(version));
                    }
                    else {
                        archives.put(archiveName, new HashSet());
                        archives.get(archiveName).add(Integer.parseInt(version));
                    }
                }
        }
        finally {
            new Close().execute(context);
        }
        return Collections.unmodifiableMap(archives);
    }
}